package hikari.gui.panels;

import com.gooddies.swing.RowLayout;
import com.gooddies.swing.hCheckBox;
import com.gooddies.texteditors.DefaultTextField;
import com.gooddies.wiring.annotations.PostWiring;
import com.gooddies.wiring.annotations.Wire;
import com.gooddies.wiring.annotations.WiringComponent;
import hikari.gui.MainWindow;
import hikari.gui.assets.AssetsTree;
import hikari.gui.guicomponents.easydrag.DefaultDrop;
import hikari.gui.guicomponents.easydrag.listeners.DropListener;
import hikari.hcomponents.HComponent;
import hikari.persistence.file.FileSection;
import hikari.script.PropertiesExtractor;
import hikari.script.ScriptProperty;
import hikari.script.propertyeditors.FloatPropertyEditor;
import hikari.script.propertyeditors.IPropertyEditor;
import hikari.script.propertyeditors.IntegerPropertyEditor;
import hikari.utils.Utils;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.tree.TreePath;
import org.apache.commons.io.FileUtils;

/**
 * @author sad
 */
@WiringComponent
public class ScriptPanel extends HComponent {

    private String scriptPath;
    private List<IPropertyEditor> properties = new ArrayList<>();
    private JPanel propertiesPanel;
    private hCheckBox enableScriptCB;
    private DefaultTextField scriptNameField;
    @Wire
    private AssetsTree assetsTree;
    @Wire
    private MainWindow mainWindow;
    @Wire
    private PropertiesExtractor extractor;
    @Wire
    private Utils utils;

    private ScriptPanel() {
    }

    @PostWiring
    private void init() {
        setTitle("Empty Script Panel");
        setLayout(new RowLayout().setStartXGap(10).setStartYGap(20).setEndYGap(10).setBetweenYGap(10));
        initDropHandler();
        enableScriptCB = new hCheckBox("Enabled");
        enableScriptCB.setSelected(true);
        add(enableScriptCB);
        scriptNameField = new DefaultTextField();
        add(scriptNameField);
        propertiesPanel = new JPanel();
        propertiesPanel.setLayout(new RowLayout().setStartYGap(5).setEndYGap(10).setBetweenYGap(10));
        add(propertiesPanel);
    }

    private void initDropHandler() {
        new DefaultDrop(this, createDropEventHandler()).setEnabled(true);
        new DefaultDrop(scriptNameField, createDropEventHandler()).setEnabled(true);
    }

    private DropListener createDropEventHandler() {
        return new DropListener() {
            @Override
            public boolean canDrop(Component source, Component target, Object data, int x, int y) {
                return data instanceof TreePath;
            }

            @Override
            public void performDrop(Component source, Component target, DropTargetDropEvent dtde, Object data) {
                if (data instanceof TreePath) {
                    TreePath path = (TreePath) data;
                    if (!assetsTree.isTreePathScript(path)) {
                        assetsTree.errorTooltip("Only script files allowed");
                        return;
                    }

                    String filePath = assetsTree.treePathToFilePath(path);
                    setScriptPath(filePath);
                }
            }
        };
    }

    @Override
    public String getHComponentName() {
        return scriptNameField.getText();
    }

    @Override
    public boolean isHComponentEnabled() {
        return enableScriptCB.isSelected();
    }

    @Override
    public void setHComponentName(String name) {
        scriptNameField.setText(name);
    }

    @Override
    public void setHComponentEnabled(boolean enabled) {
        enableScriptCB.setSelected(enabled);
    }

    @Override
    public boolean isObjectUpdateable() {
        return true;
    }

    @Override
    public JMenuItem[] submitPopupMenuItems() {
        JMenuItem resetItem = new JMenuItem("Reset");
        resetItem.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                resetPanelProperties();
            }
        });
        return new JMenuItem[]{resetItem};
    }

    @Override
    public boolean isDeleteable() {
        return true;
    }

    public String getScriptPath() {
        return scriptPath;
    }

    public void setScriptPath(String scriptPath) {
        this.scriptPath = scriptPath;
        String fileName = utils.getFileNameWithoutExtension(scriptPath);
        setTitle("Script: " + fileName);
        scriptNameField.setText(fileName);
        enableScriptCB.setSelected(true);
        resetPanelProperties();
    }

    private String getHeaderFilePath(String scriptPath) {
        String extension = utils.getFileExtension(scriptPath);
        if (extension.equals("h") || extension.equals("hpp")) {
            return scriptPath;
        }

        String filePath = utils.getFilePath(scriptPath);
        String fileName = utils.getFileNameWithoutExtension(scriptPath);
        String headerFile = filePath + fileName + ".h";
        if (new File(headerFile).exists()) {
            return headerFile;
        }

        headerFile = filePath + fileName + ".hpp";
        if (new File(headerFile).exists()) {
            return headerFile;
        }

        return null;
    }

    public void resetPanelProperties() {
        try {
            propertiesPanel.removeAll();
            if (scriptPath != null) {
                properties.clear();
                String headerPath = getHeaderFilePath(scriptPath);
                String text = FileUtils.readFileToString(new File(headerPath));
                ScriptProperty[] props = extractor.getScriptProperties(text);
                for (ScriptProperty prop : props) {
                    IPropertyEditor editor = createPropertyEditor(prop);
                    propertiesPanel.add(editor, RowLayout.NewRow);
                    properties.add(editor);
                }

                if (props.length == 0) {
                    setPreferredSize(new Dimension(getPreferredSize().width, 20 + props.length * 20 + 20));
                }
            }

            invalidate();
            validate();
            repaint();
            updateSizesInAncestors();
        } catch (IOException ex) {
            utils.logError(ex);
            utils.errorMessage(mainWindow, "Cannot extract script properties from file " + scriptPath);
        }
    }

    private IPropertyEditor createPropertyEditor(ScriptProperty property) {
        switch (property.getType().toLowerCase()) {
            case "int": {
                IntegerPropertyEditor editor = new IntegerPropertyEditor(property.getName(), 0);
                return editor;
            }
            case "float": {
                FloatPropertyEditor editor = new FloatPropertyEditor(property.getName(), 0);
                return editor;
            }
            default: {
                utils.logError("Cannot find editor for property " + property.getType());
                return null;
            }
        }
    }

    @Override
    public void writeData(FileSection section) throws IOException {
        if (scriptPath != null && !scriptPath.isEmpty()) {
            FileSection script = section.createNewSection("script");
            script.writeString("path", assetsTree.getRelativeAssetsPath(scriptPath));
            script.writeString("name", getHComponentName());
            script.writeBoolean("enabled", isHComponentEnabled());
            for (int i = 0; i < properties.size(); i++) {
                IPropertyEditor editor = properties.get(i);
                editor.save(script);
            }

            script.close();
        }
    }
}
